<?php
// +----------------------------------------------------------------------
// | Bwsaas
// +----------------------------------------------------------------------
// | Copyright (c) 2015~2020 http://www.buwangyun.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Gitee ( https://gitee.com/buwangyun/bwsaas )
// +----------------------------------------------------------------------
// | Author: buwangyun <hnlg666@163.com>
// +----------------------------------------------------------------------
// | Date: 2020-9-28 10:55:00
// +----------------------------------------------------------------------

namespace app\manage\model;

use app\common\model\User;
use buwang\base\BaseModel;
use buwang\traits\JwtTrait;
use buwang\service\UserService;
use buwang\service\CacheService;

/**token表
 * Class BwAuthNode
 * @package app\manage\model
 */
class Token extends BaseModel
{
    use JwtTrait;
    protected $pk = 'id';
    // 表名
    protected $name = 'token';
    protected $updateTime = '';

    protected $deleteTime = '';


    /**更新token信息
     * @param $uid
     * @param $token
     * @param $scopes
     * @param $request
     * @param bool $trans
     * @return bool
     */
    public static function updateToken($uid, $token, $scopes, $trans = false)
    {

        if ($trans) {
            self::startTrans();
        }
        $res = true;
        try {
            $Token = self::where('user_id', $uid)->where('scopes', $scopes)->where('token', $token)->find();
            if (!$Token) $Token = new self;
            $Token['token'] = $token;
            $Token['user_id'] = $uid;
            $Token['create_time'] = time();
            $Token['expires_time'] = time() + config('jwt.accessTokenExp');
            $Token['login_ip'] = request()->ip();
            $Token['scopes'] = $scopes;

            $res = $Token->save();
            $res = $res && true;
            if ($trans) {
                self::checkTrans($res);
            }
        } catch (\Exception $e) {
            if ($trans) {
                self::rollback();
            }
            return self::setError($e->getMessage());
        }
        if (!$res) return self::setError('更新token失败');

        return $res;
    }

    /** 检测token并返回token携带的用户信息
     * @param $token
     * @param $jwtinfo
     * @param bool $trans
     * @return array|bool|\think\Model
     */
    public static function validateToken($token, $jwtinfo, $trans = false)
    {
        $uid = $jwtinfo->data->uid;
        $scopes = $jwtinfo->scopes;
        if (!$uid) return self::setError('jwt解析uid错误', 400006);
        $base_login_mode = bw_config('base.base_login_mode', 2);//允许=>1 不允许=>2
        $base_login_number = bw_config('base.base_login_number', 600);//欲过期刷新时间 单位s
        if ($trans) {
            self::startTrans();
        }
        $res = $res1 = $res2 = $res3 = $res4 = $res5 = $res6 = true;
        try {
            $tokeninfo = self::where('user_id', $uid)->where('scopes', $scopes)->where('token', $token)->find();
            if (!$tokeninfo) return self::setError('未登录请先去登录', 400001);
            if ($tokeninfo['expires_time'] < time()) {
                //如果已过期，则不给通过，删除数据库token信息
                $res1 = self::deleteToken($token, $uid, $scopes);
                $res = $res1 && true;
                if ($trans) {
                    self::checkTrans($res);
                }
                return self::setError('登录已过期，请重新登录', 400001);
            }
//          是否开启多端登录
//          开启多端登陆  ，不用做逻辑处理
//          未开启多端登录
//          判断数据库是否已存在该类型的登录token，如果存在，判断跟客户端的token是否一致，如果不一致，拒绝访问
            if ($base_login_mode == 2) {
                $create_time = self::where('user_id', $uid)->where('scopes', $scopes)->max('create_time');
                $tokensolo = self::where('create_time', $create_time)->where('user_id', $uid)->where('scopes', $scopes)->find();
                if ($token != $tokensolo['token']) return self::setError('当前用户已在另一端登录', 400002);
            }
            $expires_time = $tokeninfo['expires_time'] - $base_login_number;
            if ($expires_time < 0) $expires_time = 0; // token过期时间戳
            $user = null;
            switch ($scopes) {
                case "admin":
                    $user = Admin::find($uid);
                    if (!$user) return self::setError('用户信息不存在', 400003);
                    if (!$user['status']) return self::setError('用户被锁定', 400005);
                    if ($tokeninfo['expires_time'] < time()) {
                        //如果已过期，则不给通过，删除数据库token信息
                        $res2 = self::deleteToken($token, $uid, $scopes);

                    } elseif ($expires_time < time()) {
                        //如果即将过期，刷新token过期时间，更新数据库token信息,重新发token
                        $res3 = Admin::loginAdmin($user);

                    }
                    break;

                case "member":
                    $user = Member::find($uid);
                    //逻辑处理
                    if (!$user) return self::setError('用户信息不存在', 400003);
                    if (!$user['status']) return self::setError('用户被锁定', 400005);
                    //查询顶级租户信息
                    //$top_user = Member::getTop($uid);
                    if (!$user['top_id']) return self::setError('用户信息验证失败，上级租户信息异常', 400004);
                    if ($tokeninfo['expires_time'] < time()) {
                        //<2>如果已过期，则不给通过，删除cookie , session,数据库token信息
                        $res4 = self::deleteToken($token, $uid, $scopes);

                    } elseif ($expires_time < time()) {
                        // <1>如果即将过期，刷新token过期时间，更新数据库token信息,重新发token
                        $res5 = Member::loginMember($user);
                    }
                    break;
                case "mini_program":
                case "app":
                case "tt":
                case "official":
                case "h5":
                    $user = User::find($uid);
                    //逻辑处理
                    if (!$user) return self::setError('用户信息不存在', 400003);
                    if (!$user['status']) return self::setError('用户被锁定', 400005);
                    break;
            }

            $res = $res && $res1 && $res2 && $res3 && $res4 && $res5 && $res6 && true;

            if ($trans) {
                self::checkTrans($res);
            }
        } catch (\Exception $e) {
            if ($trans) {
                self::rollback();
            }
            return self::setError($e->getMessage());
        }
        if (!$res) return self::setError('检测token失败', 400444);
        return $user;
    }

    /** 检测token并返回token携带的用户信息
     * @param $token
     * @param $jwtinfo
     * @param bool $trans
     * @return array|bool|\think\Model
     */
    public static function validateRedisToken($token, $jwtinfo)
    {
        $uid = $jwtinfo->data->id;
        $scopes = $jwtinfo->scopes;
        if (!$uid) return self::setError('jwt解析uid错误', 400006);
        $userType = in_array($scopes, BW_CLIENT_TYPE) ? 'user' : $scopes;//user对应bw_user表，member对应bw_member表，admin对应表bw_admin
        $redisKey = $userType . $uid;

        $res = $res1 = $res2 = $res3 = $res4 = $res5 = $res6 = true;
        $user = null;
        try {
            //获取redis中存储的当前登录用户token对应的信息
            $tokenData = app('cacheService')->getTokenCache($token);

            if (!isset($tokenData)) return self::setError('登录已过期或在别处登录', 400001);
            //开启多人同时登录后，则获取的是token数组
            $tokens = app('cacheService')->getTokenCache($redisKey);
            if (!isset($tokens)) return self::setError('登录已过期', 400001);
            switch ($scopes) {
                case "admin"://总平台管理员
                    $user = Admin::find($uid);
                    if (!$user) return self::setError('用户信息不存在', 400001);
                    if (!$user['status']) return self::setError('用户被锁定', 400005);
                    break;

                case "member"://租户
                    $user = Member::find($uid);
                    //逻辑处理
                    if (!$user) return self::setError('用户信息不存在', 400001);
                    if (!$user['status']) return self::setError('用户被锁定', 400005);
                    //查询顶级租户信息
                    if (!$user['top_id']) return self::setError('用户信息验证失败，上级租户信息异常', 400004);
                    break;
                case "mini_program"://租户应用的微信小程序端
                case "app"://租户应用的app端
                case "official"://租户应用的公众号端
                case "h5"://租户应用的h5端
                case "tt"://租户应用头条端
                case "pc"://租户应用的pc端
                    $user = User::find($uid);
                    //逻辑处理
                    if (!$user) return self::setError('用户信息不存在', 400001);
                    if (!$user['status']) return self::setError('用户被锁定', 400005);
                    break;
            }

            $res = $res && $res1 && $res2 && $res3 && $res4 && $res5 && $res6 && true;

        } catch (\Exception $e) {
            return self::setError($e->getMessage());
        }
        if (!$res) return self::setError('检测token失败', 400444);
        return $user;
    }


    public static function deleteToken($token = '', $uid = '', $scopes = '', $trans = false)
    {
        if ($trans) {
            self::startTrans();
        }
        $res = $res1 = $res2 = true;
        try {
            if (!$token) $token = cookie('token');
            if (!$token) return self::setError('缺少token');
            if (!$uid || !$scopes) {
                $jwtinfo = self::decodeToken($token);//解析token,获取用户信息;
                if ($jwtinfo) {
                    if (!$scopes) $scopes = $jwtinfo->scopes;
                    if (!$uid) $uid = $jwtinfo->data->uid;
                } else {
                    return self::setError('缺少必要参数');
                }
            }
            //清除session
            UserService::setLogout();
//            //清除cookie
//            cookie('token', null);
            //删除数据库token
            $tokeninfo = self::where('user_id', $uid)->where('scopes', $scopes)->where('token', $token)->find();
            if ($tokeninfo) {
                $res2 = self::destroy($tokeninfo['id'], true);//真实删除
            }


            $res = $res && $res1 && $res2 && true;

            if ($trans) {
                self::checkTrans($res);
            }
        } catch (\Exception $e) {
            if ($trans) {
                self::rollback();
            }
            return self::setError($e->getMessage());
        }
        if (!$res) return self::setError('删除token失败');

        return $res;

    }

    public static function deleteRedisToken($token = '', $uid = '', $scopes = '')
    {
        $res = $res1 = $res2 = $res3 = true;
        try {
            if (!$token) return self::setError('缺少token');
            if (!$uid || !$scopes) {
                $jwtInfo = self::decodeToken($token);//解析token,获取用户信息;
                if ($jwtInfo) {
                    $scopes = $jwtInfo->scopes;
                    $uid = $jwtInfo->data->id;
                } else {
                    return self::setError('缺少必要参数,解析token出错');
                }
            }
            //清除session
            UserService::setLogout();
            //删除REDIS数据库token
            //redis入库key值 防止后台和前端用户ID重复
            $userType = in_array($scopes, ['mini_program', 'app', 'h5', 'official', 'pc']) ? 'user' : $scopes;
            $redisKey = $userType . $uid;

            $tokens = CacheService::getTokenCache($redisKey);//获取token数组

            foreach ($tokens as $key => $value) {
                if ($tokens[$key] == $token) {
                    if (count($tokens) == 1) {
                        $res1 = CacheService::clearToken($token);
                        $res2 = CacheService::clearToken($redisKey);
                    } else {
                        $res3 = CacheService::clearToken($token);
                        unset($tokens[$key]);
                    }
                }//清除对应的token
            }
            $res = $res1 && $res2 && $res3 && true;
        } catch (\Throwable $e) {
            return self::setError('删除token失败:' . $e->getMessage());
        }
        if (!$res1) return self::setError('删除token失败1');
        if (!$res2) return self::setError('删除token失败2');
        if (!$res3) return self::setError('删除token失败3');
        return $res;

    }


}
